/*-------------------------------------------------------------------------
 - Copyright (c) 2024-2025 [XD-AMCC TEAM]
 - [XD-AMCC] is licensed under Mulan PSL v2.
 - You can use this software according to the terms and conditions of the Mulan PSL v2.
 - You may obtain a copy of Mulan PSL v2 at:
 -          http://license.coscl.org.cn/MulanPSL2
 - THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES
 - OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
 - TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 - See the Mulan PSL v2 for more details.
 ------------------------------------------------------------------------*/


/**
 * @brief  Semantic actions of declarations for building symbol tables.
 *
 * @author  WXQ#XDU
 * @date    2024.12
 */

#include "_stb_inner.h"


// pre-semantic-action for translationUnit:
//   Build the top-most symbol table which stores all global names;
//   PUSH this table onto the scope stack.
//
void stb_enter_translationUnit(t_tree_listener_ptr listenerPtr,
                               t_tree_ptr node)
{
    t_symtable_builder * builderPtr = (t_symtable_builder *)listenerPtr;
    builderPtr->nErrors = error_get_count();

    log_trace("CREATing the TOP-MOST symTABLE, tree-node=%tx", node);

    builderPtr->symtableRootPtr = symtable_new(node, NULL);
    SCOPE_STACK_PUSH(builderPtr, builderPtr->symtableRootPtr, NULL);

    LOG_STEP ("SCOPE-STACK: PUSH the TOP-MOST symTABLE %tx, tree-node=%tx",
              builderPtr->symtableRootPtr, node);
}


// post-semantic-action for translationUnit:
//  Resolve all unresolved function in the global scope;
//  POP the top-most symbol table from the scope stack.
//
void stb_exit_translationUnit(t_tree_listener_ptr listenerPtr, t_tree_ptr node) {
    t_symtable_builder * builderPtr = (t_symtable_builder *)listenerPtr;

    stb_resolveAllUndefinedFunctions(&(builderPtr->undefinedFunctions),
                                     builderPtr->symtableRootPtr);
    list_clean(&(builderPtr->undefinedFunctions));

    builderPtr->nErrors = error_get_count() - builderPtr->nErrors;

    SCOPE_STACK_POP(builderPtr);
    LOG_STEP ("SCOPE-STACK: POP the TOP-MOST symTABLE %tx, tree-node=%tx",
              builderPtr->symtableRootPtr, node);

}


// pre-semantic-action for functionDeclaration:
// (1) Creates function's symbol table that stores parameters,
//     labels and variables declared in the outermost block
//     in function body.
// (2) PUSH the new symbol table onto the scope stack.
//
void stb_enter_functionDeclaration(t_tree_listener_ptr listenerPtr,
                                   t_tree_ptr node)
{
    t_symtable_builder * builderPtr = (t_symtable_builder *)listenerPtr;
    t_tree_ptr funcNameNode = tree_get_child(node, 1); // ID node
    log_trace("CREATING symTABLE of function \"%s()\", tree-node=%tx",
              tree_get_markText(funcNameNode), node);

//1. creates the symbol table of current function
    t_stack_item_ptr stackItemPtr = SCOPE_STACK_TOP(builderPtr);
    t_symtable_ptr parentSymTable = stackItemPtr->symtablePtr;
    t_symtable_ptr funcSymTable = symtable_new( funcNameNode,
                                                parentSymTable );

//2. creates information of current function,
//   it will be used by children nodes

    // records the return type
    t_tree_ptr typeSpecifierNode = tree_get_child ( node, 0 );
    t_tree_ptr typeNameNode      = tree_get_child ( typeSpecifierNode, 0 );
    t_funcinfo_ptr funcinfoPtr   = stb_new_funcinfo(
                tree_get_tokenKind(typeNameNode),
                funcSymTable,
                tree_get_markText(funcNameNode) );


//3. push this function's table onto the stack

    SCOPE_STACK_PUSH(builderPtr, funcSymTable, funcinfoPtr);
    LOG_STEP ("SCOPE-STACK: PUSH symTABLE %tx of function \"%s()\", tree-node=%tx",
              funcSymTable, tree_get_markText(funcNameNode), node);

}

// post-semantic-action for functionDefinition:
// (1) Resolve all un-resolved labels used in current function.
// (2) POP the scope-stack.
//
void stb_exit_functionDeclaration(t_tree_listener_ptr listenerPtr,
                                  t_tree_ptr node)
{
    t_symtable_builder * builderPtr = (t_symtable_builder *)listenerPtr;

// binding all un-resolved labels in current function
    t_stack_item_ptr stackItemPtr = SCOPE_STACK_TOP(builderPtr);
    t_symtable_ptr   funcSymTable = stackItemPtr->symtablePtr;
    t_funcinfo_ptr   funcinfoPtr  = stackItemPtr->currFuncInfoPtr;
    stb_resolveAllUndefinedLabels(&(funcinfoPtr->undefinedLabels), funcSymTable);

// POP this function's table from the stack
    SCOPE_STACK_POP(builderPtr);

    LOG_STEP ("SCOPE-STACK: POP symTABLE %tx of function \"%s()\", tree-node=%tx",
              funcSymTable, funcinfoPtr->funcName, node);
    stb_free_funcinfo(funcinfoPtr);

    // In the textbook, the symbol-entry of a function is created AT HERE.
    // But in AMCC, to support nested functions,
    // this work is performed in exit_parameterList() below :::
    // At that moment, the param-list has just been analyzed,
    // but the function body had not yet been analyzed.

}


// post-semantic-action for parameterList:
//   Stores its function name as a symbol entry into the
//          symbol table of the enclosing scope.
// NOTICE:
//   For a function without parameters, its parameterList tree-node
//       SHOULD have ONLY ONE child marked by EPSILON.
//   See  Parser::RDF_parameterList()
//
void stb_exit_parameterList(t_tree_listener_ptr listenerPtr, t_tree_ptr node) {
    t_symtable_builder * builderPtr = (t_symtable_builder *)listenerPtr;

    // get the stack element pushed in  enter_functionDeclaration()
    t_stack_item_ptr    stackItemPtr = SCOPE_STACK_TOP(builderPtr);
    t_symtable_ptr      funcSymTable = stackItemPtr->symtablePtr;
    t_funcinfo_ptr      funcinfoPtr  = stackItemPtr->currFuncInfoPtr;
    t_func_symInfo_ptr  pFuncSymInfo = funcinfoPtr->funcSymInfoPtr;

    // create the type of this function
    pFuncSymInfo->typePtr = type_new_funcType (
                type_new_baseType(funcinfoPtr->returnType),
                &(pFuncSymInfo->parameterList) );

    // create and store the symbol entry of this function
    t_tree_ptr         parentNode = tree_get_parent(node); // funcDeclaration
    t_symtable_ptr targetSymTable = funcSymTable->parent;
    t_symentry_ptr      pSymEntry = symtable_putFuncDefine(
                    targetSymTable,
                    tree_get_child(parentNode, 1), // ID node
                    pFuncSymInfo);
    if (NULL != pSymEntry) {
        LOG_STEP ("put symENTRY %tx of function \"%s\" into symTABLE %tx of scope %s",
                  pSymEntry,  funcinfoPtr->funcName,  targetSymTable,
                  tree_get_markText(targetSymTable->ownerTreeNode));
    } else {
        log_error("failed to record symentry of function \"%s\"",
                  funcinfoPtr->funcName);
    }
}


// set builderPtr->baseType
static
void stb_set_basetype__(t_tree_listener_ptr listenerPtr, t_tree_ptr typeSpecifierNode) {
    t_symtable_builder * builderPtr = (t_symtable_builder *)listenerPtr;
    EnumTokenKind  typeId;  // type of variable or array element

    // get the type token for variable, and check it
    {
        t_tree_ptr typeNameNode = tree_get_child (typeSpecifierNode, 0);
        typeId = tree_get_tokenKind(typeNameNode);
        if ( TK_TYPE_VOID == typeId ) { // Check var type: NO 'void'
            t_position_ptr pos = token_get_position(tree_get_token(typeNameNode));
            ERROR_REPORT_INPUT(pos->filePath, pos->line, pos->column, EC_VAR_VOID );

            // ignore error, and assuming this type to continue analysis
            typeId = STB_ASSUME_TYPE;
        }
    }

    // set the base type of current variables
    builderPtr->baseType = type_new_baseType(typeId);
    builderPtr->dimensions = NULL;
    builderPtr->indexHolders = NULL;
    builderPtr->initExprs = NULL;
    builderPtr->initorInDim = 0;
}


// pre-semantic-action for parameter:
//   Collectd the basic type, and stored it in the builder.
//
void stb_enter_parameter(t_tree_listener_ptr listenerPtr, t_tree_ptr node) {
    t_tree_ptr  typeSpecNode  = tree_get_child(node, 0); // typeSpecifier
    stb_set_basetype__(listenerPtr, typeSpecNode);
}


// post-semantic-action for parameter:
//   Stores parameters as a symbol entry
//   into current function's symbol table.
//
void stb_exit_parameter(t_tree_listener_ptr listenerPtr, t_tree_ptr node) {
    t_symtable_builder * builderPtr = (t_symtable_builder *)listenerPtr;

    // Get the attribute of directDeclarator
    t_tree_ptr            declNode = tree_get_child(node, 1);
    t_attr_directDeclarator * attr = (t_attr_directDeclarator *)
            treemap_get(builderPtr->nodeAttributes, declNode);
    stb_array_checkSize(attr);   // ignore error at here

    // Creates the type of parameter
    t_type_ptr paramType = type_new_varType(builderPtr->baseType, attr->dimensions);

    // Allocates memory for parameter:
    // - specify the memory layout
    // - get the offset in memory space
    size_t offset;
    t_stack_item_ptr stackItemPtr = SCOPE_STACK_TOP(builderPtr);
    t_symtable_ptr targetSymTable = stackItemPtr->symtablePtr;  // function's table
    stb_allocMemoryForVariable(targetSymTable, paramType, &offset);

    // Creates the symbol entry of parameter, and put into the symbol table
    t_symentry_ptr pSymEntry = symtable_putVarDefine(
            targetSymTable, paramType,
            attr->idNode, offset,
            attr->initializerNode);
    if (NULL != pSymEntry) {
        t_list_ptr  paramList = &(stackItemPtr->currFuncInfoPtr
                                    ->funcSymInfoPtr->parameterList);
        list_append( paramList,  pSymEntry );
        LOG_STEP ("put symENTRY %tx of parameter \"%s\" of %s() into"
                  " symTABLE %tx of function %s",
                  pSymEntry, tree_get_markText(attr->idNode),
                  stackItemPtr->currFuncInfoPtr->funcName, targetSymTable,
                  tree_get_markText(targetSymTable->ownerTreeNode));
    } else {
        log_error("failed to record symentry of parameter \"%s\" of %s()",
                  tree_get_markText(attr->idNode),
                  stackItemPtr->currFuncInfoPtr->funcName);
    }

    // destroy the attribute of directDeclarator
    treemap_remove(builderPtr->nodeAttributes, declNode );

}  // end of stb_exit_parameter()


// Helper: creates a symbol-entry for a variable,
//     and stores it into the scope-symbol-table.
static void stb_create_var_symbol(t_symtable_builder * builderPtr,
                                  t_tree_ptr    initDeclaratorNode)
{
    // get the attribute of initDeclarator
    t_attr_directDeclarator * attr = (t_attr_directDeclarator *)
            treemap_get(builderPtr->nodeAttributes, initDeclaratorNode);
    stb_array_checkSize(attr);   // ignore error at here

    // create the type for current variable
    t_type_ptr varType = type_new_varType(builderPtr->baseType, attr->dimensions);

    // create the symbol entry for current variable name
    size_t offset;
    t_symtable_ptr targetSymTable = SCOPE_STACK_TOP(builderPtr)->symtablePtr;
    stb_allocMemoryForVariable(targetSymTable, varType, &offset);
    t_symentry_ptr pSymEntry = symtable_putVarDefine(
            targetSymTable, varType,
            attr->idNode, offset,
            attr->initializerNode);
    if (NULL != pSymEntry) {
        // will be destroyed with sym-entry, see symtable.c::mapfunc_free()
        pSymEntry->content.varInfo->initExprs = attr->initExprs;
        // will be destroyed in stb_free_initExpr_(), if NOT NULL
        attr->initExprs = NULL;

        bool isFunc = tree_is_funcNameDefinition(targetSymTable->ownerTreeNode);
        LOG_STEP ("put symENTRY %tx of %s \"%s\" into symTABLE %tx of %s %s",
                  pSymEntry,
                  VAR_IS_ARRAY(attr->dimensions) ? "array" : "variable",
                  tree_get_markText(attr->idNode), targetSymTable,
                  (isFunc ? "function" : "scope"),
                  tree_get_markText(targetSymTable->ownerTreeNode));
    } else {
        log_error("failed to record symentry of %s \"%s\" into symTABLE %tx",
                  VAR_IS_ARRAY(attr->dimensions) ? "array" : "variable",
                  tree_get_markText(attr->idNode),
                  targetSymTable);
    }

    {  // set the type to help semantic analysis later
        EXPRTYPE_STORE(initDeclaratorNode, varType);

        t_tree_ptr directDeclNode = tree_get_child(initDeclaratorNode, 0);
        EXPRTYPE_STORE(directDeclNode, varType);

        t_tree_ptr assignOptrNode = tree_get_child(initDeclaratorNode, 1);
        if (NULL != assignOptrNode) {
            EXPRTYPE_STORE(assignOptrNode, varType);
        }
    }

    // write a file which store init-value for arrays
    if ( (false == attr->hasError) && (NULL != attr->initializerNode)) {
        array_init_dump( pSymEntry );
    }

    // destroy the attribute of initDeclarator
    treemap_remove( builderPtr->nodeAttributes, initDeclaratorNode );
}


// pre-semantic-action for variableDeclaration:
//   Collects the basic type, and stores it in the builder.
//
void stb_enter_variableDeclaration(t_tree_listener_ptr listenerPtr, t_tree_ptr node) {
    t_tree_ptr  typeSpecNode  = tree_get_child(node, 0); // typeSpecifier
    stb_set_basetype__(listenerPtr, typeSpecNode);
}


// post-semantic-action for variableDeclaration:
//   Creates a symbol entry for each variable defined in the current statement;
//   Stores each entry into the symbol table of the current scope.
//
void stb_exit_variableDeclaration(t_tree_listener_ptr listenerPtr, t_tree_ptr node) {
    t_symtable_builder * builderPtr = (t_symtable_builder *)listenerPtr;

    // For production :  varDecl -> typeSpec initDecl ( , initDecl )*
    //  create the symbol entry for each variable in 'initDecl'
    t_list_iterator itr;
    LIST_ITERATOR_INIT( &itr, tree_get_children(node) );
    t_list_node_ptr  listNode  = LIST_ITERATOR_BEGIN( &itr );
    t_list_node_ptr  listEnd   = LIST_ITERATOR_END( &itr );
    listNode = LIST_ITERATOR_NEXT( &itr );      // SKIP the typeSpecifier
    for( ; listNode != listEnd; listNode = LIST_ITERATOR_NEXT( &itr ) ) {
        t_tree_ptr initDeclaratorNode = (t_tree_ptr)list_node_get_value(listNode);
        t_nonterminal_ptr  nt = tree_get_nonTerminal(initDeclaratorNode);
        if ( NULL == nt ) continue;  // this is a comma

        stb_create_var_symbol(builderPtr, initDeclaratorNode);
    }  // for each initDeclarator
}


// post-semantic-action for initDeclarator:
//   Pass variable/array information in bottom-up.
//
void stb_exit_initDeclarator(t_tree_listener_ptr listenerPtr, t_tree_ptr node) {
    t_symtable_builder * builderPtr = (t_symtable_builder *)listenerPtr;
    t_tree_ptr   declNode = tree_get_child(node, 0);  // directDeclarator

    // get the attribute of directDeclarator
    t_attr_directDeclarator * attr;
    attr = (t_attr_directDeclarator *)
            treemap_get(builderPtr->nodeAttributes, declNode);

    // initDeclarator: var = initializer
    if (tree_get_childCount(node) == 3)
        attr->initializerNode = tree_get_child(node, 2);

    {  // transfer information of array
        if ( NULL != attr->dimensions ) {
            attr->initExprs     = builderPtr->initExprs;
            if ( attr->dimensions[1] < 1)
                attr->dimensions[1] = builderPtr->lenOfDim1;
            if ( attr->dimensions[1] < 1) { // missing the size of the 1st dimension
                t_position   pos = tree_get_startPosition(attr->idNode);
                ERROR_REPORT_INPUT(pos.filePath, pos.line, pos.column,
                                   EC_DIMSZ_MISS, 1);
                attr->hasError = true;
            }

        }

        // reset helper data that has set in
        // stb_enter_initializer() and stb_exit_directDeclarator()
        builderPtr->initorInDim  = 0;
        builderPtr->initExprs  = NULL;
        builderPtr->dimensions = NULL;
        if (NULL != builderPtr->indexHolders) {
            free(builderPtr->indexHolders);
            builderPtr->indexHolders = NULL;
        }
    }

    // pass the attribute from directDeclarator to varDeclaration
    //
    // (1) make a pseudo copy firstly and (2)
    treemap_put(builderPtr->nodeAttributes, node,
                ref_copy_((t_reference_ptr) attr));
    // (2) release last pseudo copy
    treemap_remove( builderPtr->nodeAttributes, declNode );
}


// post-semantic-action for directDeclarator:
//   Collects information of array dimensions
//   for var-definition or parameter
//
void stb_exit_directDeclarator(t_tree_listener_ptr listenerPtr, t_tree_ptr node) {
    t_symtable_builder * builderPtr = (t_symtable_builder *)listenerPtr;
    t_attr_directDeclarator * attr  = new_attr_directDeclarator();
    attr->idNode  = tree_get_child(node, 0); // holds ID token

    // For any array, collects its dimension, and size of each dimensions
    int nChildren = tree_get_childCount(node);
    if ( nChildren > 1) {  // children: ID[][d2]..., or ID[d1][d2]...
        int nDims = nChildren / 3; // number of dimensions
        attr->dimensions = calloc(nDims + 1, sizeof(int));
        attr->dimensions[0] = nDims; // number of dimensions

        t_list_iterator itr;   // Following: fill in the length of each dim
        LIST_ITERATOR_INIT( &itr, tree_get_children(node));
        t_list_node_ptr child    = LIST_ITERATOR_BEGIN( &itr );
        t_list_node_ptr childEnd = LIST_ITERATOR_END( &itr );
        child = LIST_ITERATOR_NEXT(&itr);  // jump the ID
        token_intvalue_t dimLen = 0, dimCounter = 1;
        for ( ; child != childEnd; child = LIST_ITERATOR_NEXT(&itr)) {
            t_tree_ptr       tkNode = (t_tree_ptr)list_node_get_value(child);
            t_token_ptr       token = tree_get_token(tkNode);
            EnumTokenKind tokenKind = token_get_kind(token);

            if ( TK_LBRACKET == tokenKind ) {           // see "["
                dimLen = 0;
            } else if ( TK_INT_LITERAL == tokenKind ) { // length
                dimLen = token_get_intValue(token);
            } else if ( TK_RBRACKET == tokenKind ) {    // see "]"
                if ((dimLen < 0) || (dimLen == 0 && dimCounter > 1) ) {
                    t_position_ptr   pos = token_get_position(token);
                    ERROR_REPORT_INPUT(pos->filePath, pos->line, pos->column,
                                       EC_DIMSZ_BAD, dimLen, dimCounter);
                    // ignore the error for continue to analysis
                    dimLen = 100; // big enough for analyzing its initializer
                    attr->hasError = true;
                }
                attr->dimensions [dimCounter ++] = dimLen;
            }
        }

        // If the current array is a function's parameter,
        // the length of the first dimension is ALWAYS zero.
        {
            t_tree_ptr parentNode = tree_get_parent(node);
            t_nonterminal_ptr contextNT = tree_get_nonTerminal(parentNode);
            if (NT_parameter == contextNT->ntId) {
                attr->dimensions[1] = 0;
            }
        }
    }

    //NOTICE:
    // attr->dimensions[1] is 0 for an incomplete array type;
    // if so, builderPtr->lenOfDim1 will be increased during
    //        analyzing the initializer of an array.
    builderPtr->dimensions = attr->dimensions;
    if (NULL != attr->dimensions)
        builderPtr->lenOfDim1  = attr->dimensions[1];
    else
        builderPtr->lenOfDim1  = 0;
    builderPtr->initorInDim  = 0;

    // the attribute will be passed in bottom-up:
    // (1) to initDeclarator, then to variableDeclaration, or
    // (2) to parameter,  and
    // (3) to initializer in left-to-right manner
    treemap_put(builderPtr->nodeAttributes, node, attr);
}


// pre-semantic-action for initializer:
//   prepare indexes of this initializer for array,
//   prepare the list of init-value-expressions for arrays,
//   CHECK the initializer-list for arrays.
//
void stb_enter_initializer(t_tree_listener_ptr listenerPtr, t_tree_ptr node) {
    t_symtable_builder * builderPtr = (t_symtable_builder *)listenerPtr;
    t_tree_ptr        braceNode = tree_get_child(node, 0); // for '{'
    t_tree_ptr       parentNode = tree_get_parent(node);
    t_nonterminal_ptr contextNT = tree_get_nonTerminal(parentNode);

    t_attr_directDeclarator * attr;  {  // get my attribute
        t_tree_ptr  nodeWithAttr;
        if ( NT_initDeclarator == contextNT->ntId ) {
            // CASE 1: the top-level initializer of an array
            // get the attribute of directDeclarator, left-to-right
            nodeWithAttr = tree_get_child(parentNode, 0);
        } else {
            // CASE 2: a sub-initializer
            // get the attribute of parent initializer, top-down
            nodeWithAttr = parentNode;
        }
        attr = (t_attr_directDeclarator *)
                treemap_get(builderPtr->nodeAttributes, nodeWithAttr);
    }
    // bound with a pseudo copy of attribute object
    treemap_put(builderPtr->nodeAttributes, node,
                ref_copy_((t_reference_ptr) attr));

    // IMPORTANT: Don't report error again for the current array,
    //        and Don't flow down
    if (NULL != attr && attr->hasError )
        return;

// CASE 1: the top-level initializer of an array,
//         initDeclarator -> directDeclarator '=' initializer
    if ( NT_initDeclarator == contextNT->ntId ) {
        EnumTokenKind tokenKind = tree_get_tokenKind(braceNode);
        if (  NULL == builderPtr->dimensions  ) {  // CASE 1: For a scalar
            // this initializer MUST NOT be a brace-enclosed list
            if ( TK_LBRACE == tokenKind ) {
                t_position pos = tree_get_startPosition(braceNode);
                ERROR_REPORT_INPUT(pos.filePath, pos.line, pos.column, EC_INIT_HASBRACE);
            }

        } else {                                  // CASE 2: For an array
            //  this initializer MUST be a brace-enclosed list
            if ( TK_LBRACE != tokenKind )  {
                attr->hasError = true;
                t_position  pos = tree_get_startPosition(braceNode);
                ERROR_REPORT_INPUT(pos.filePath, pos.line, pos.column, EC_INIT_NOBRACE, 1);
                return;
            }

            // prepare data for analyzing this initializer of array
            builderPtr->initorInDim = 1;
            builderPtr->initExprs = new_init_expr();
            builderPtr->indexHolders = calloc(builderPtr->dimensions[0] + 1, sizeof(int));
            builderPtr->indexHolders[0] = builderPtr->dimensions[0];

            // init all indexes for the first element
            for (int k = 1; k <= builderPtr->dimensions[0]; ++k)
                builderPtr->indexHolders[k] = 0;
        }

        return;
    }

    if ( NULL == builderPtr->dimensions ) return;
    // following is ONLY applied to initializers of array

// CASE 2: a sub-initializer, NT_initDeclarator != contextNT->ntId

    int       currentDim = builderPtr->initorInDim;
    int  idxOfCurrentDim = builderPtr->indexHolders[currentDim];
    int  szOfCurrentDim  = builderPtr->dimensions  [currentDim];
    { // CHECK: Have we seen too-many initializers ???
        if (currentDim == 1 && 0 == builderPtr->dimensions[1]) {
            // increase the length of the first dimension
            builderPtr->lenOfDim1 += 1;
        } else if (idxOfCurrentDim >= szOfCurrentDim) { // too many initializer
            attr->hasError = true;
            t_position pos = tree_get_startPosition(node);
            ERROR_REPORT_INPUT(pos.filePath, pos.line, pos.column, EC_INIT_TOOLEN,
                               idxOfCurrentDim + 1,
                               szOfCurrentDim, currentDim);
            return;
        }
    }

// CASE 2.1: a sub-initializer-list enclosed by { }
    if ( TK_LBRACE == tree_get_tokenKind(braceNode) ) {
        currentDim = ++(builderPtr->initorInDim);

        // CHECK: Is the nesting-brace too deep ???
        if( currentDim > builderPtr->dimensions[0] ) {
            attr->hasError = true;
            t_position  pos = tree_get_startPosition(braceNode);
            ERROR_REPORT_INPUT(pos.filePath, pos.line, pos.column, EC_INIT_TOODEEP,
                               currentDim, builderPtr->dimensions[0]);
            return;
        }

        for (int k = currentDim; k <= builderPtr->dimensions[0]; ++k)
            builderPtr->indexHolders[k] = 0;

        return;
    }

// case 2.2  this initializer is an expression, its value is a scalar

    // CHECK:  for a sub-array, its initializer MUST be a brace-enclosed list
    if (builderPtr->initorInDim < builderPtr->dimensions[0]) {
        attr->hasError = true;
        t_position  pos = tree_get_startPosition(braceNode);
        ERROR_REPORT_INPUT(pos.filePath, pos.line, pos.column, EC_INIT_NOBRACE,
                           builderPtr->initorInDim + 1);
        return;
    }

}


// Records the init-expression node of a scalar element of the current array
static void stb_record_init_expr(t_symtable_builder * builderPtr,
                                 t_tree_ptr  exprNode,
                                 t_attr_directDeclarator * attr)
{
    size_t           n_slots = builderPtr->indexHolders[0] + 1; // dimension + 1
    t_init_expr_ptr initExpr = calloc(1, sizeof(t_init_expr));
    initExpr->exprNode       = exprNode;

    // set indexes of the element corresponding to this init-expr
    initExpr->indexHolders = calloc(n_slots, sizeof(int));
    memcpy(initExpr->indexHolders, builderPtr->indexHolders, sizeof(int) * n_slots);
    list_append(builderPtr->initExprs, initExpr);
}

// post-semantic-action for initializer:
//   Set the type of initializer if its child is an expression
//   for var-definition only
//
void stb_exit_initializer(t_tree_listener_ptr listenerPtr, t_tree_ptr node) {
    t_symtable_builder * builderPtr = (t_symtable_builder *)listenerPtr;

    // get my attribute, set by stb_enter_initializer()
    t_attr_directDeclarator * attr = (t_attr_directDeclarator *)
            treemap_get(builderPtr->nodeAttributes, node);
    if (NULL != attr && attr->hasError)  // IMPORTANT
        return;

    int nChildren = tree_get_childCount(node);

// CASE 1: this_initializer -> expression, for a scalar, or basic element of array
    if (nChildren == 1) {
        t_type_ptr  baseType = type_var_base(builderPtr->baseType);
        t_tree_ptr  exprNode = tree_get_child(node, 0);
        t_type_ptr  exprType = EXPRTYPE_GET(exprNode);
        EXPRTYPE_STORE(node, exprType);
        if (false == type_is_compatible(baseType, exprType,  false)) {
            t_position  pos = tree_get_startPosition(exprNode);
            ERROR_REPORT_INPUT(pos.filePath, pos.line, pos.column,
                               EC_INIT_TYPE, type_name(exprType) );
            if (NULL != attr ) attr->hasError = true;
        }

        // For an array, records this init-value-expression
        if (NULL != builderPtr->dimensions && false == attr->hasError) {
            stb_record_init_expr(builderPtr, exprNode, attr);
        }
    }

    if (NULL != builderPtr->dimensions) { // index of next element
        ++(builderPtr->indexHolders[builderPtr->initorInDim]);
    }

// CASE 2: this_initializer -> '{' (initializer)*  '}'
    if (nChildren > 1 && NULL != builderPtr->dimensions) {
        // this is the end of current sub-array
        builderPtr->initorInDim -= 1;
        ++(builderPtr->indexHolders[builderPtr->initorInDim]);
    }

    // release attribute object, set by stb_enter_initializer()
    treemap_remove( builderPtr->nodeAttributes, node );
}
